package GUI;

import java.util.ArrayList;
import java.util.HashMap;


public class GraphNetwork {
	private class Edge{
		// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
		// Based on the GraphLayout example by Sun Microsystems.
		Node from;
		Node to;
		float len;
		int count;
		
		////////////////////////////////////////////////////////////////////////////////////////////////////
		Edge(Node from, Node to) {
			this.from = from;
			this.to = to;
			this.len = 50;
		}
		void increment() {
			count++;
		}
		void relax() {
			float vx = to.x - from.x;
			float vy = to.y - from.y;
			float d = Utils.globalProcessing.mag(vx, vy);
			if (d > 0) {
				float f = (len - d) / (d * 3);
				float dx = f * vx;
				float dy = f * vy;
				to.dx += dx;
				to.dy += dy;
				from.dx -= dx;
				from.dy -= dy;
			}
		}


		void draw() {
			Utils.globalProcessing.stroke(edgeColor);
			Utils.globalProcessing.strokeWeight((float)0.35);
			Utils.globalProcessing.line(from.x, from.y, to.x, to.y);
		}
	}

	private class Node{
		// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
		// Based on the GraphLayout example by Sun Microsystems.


		float x, y;
		float dx, dy;
		boolean fixed;
		String label;
		int count;
		
		////////////////////////////////////////////////////////////////////////////////////////////////////
		Node(String label) {
			this.label = label;
			x = Utils.globalProcessing.random(Utils.globalProcessing.width);
			y = Utils.globalProcessing.random(Utils.globalProcessing.height);
		}


		void increment() {
			count++;
		}


		void relax() {
			float ddx = 0;
			float ddy = 0;

			for (int j = 0; j < nodes.size(); j++) {
				Node n = nodes.get(j);
				if (n != this) {
					float vx = x - n.x;
					float vy = y - n.y;
					float lensq = vx * vx + vy * vy;
					if (lensq == 0) {
						ddx += Utils.globalProcessing.random(1);
						ddy += Utils.globalProcessing.random(1);
					} else if (lensq < 100*100) {
						ddx += vx / lensq;
						ddy += vy / lensq;
					}
				}
			}
			float dlen = Utils.globalProcessing.mag(ddx, ddy) / 2;
			if (dlen > 0) {
				dx += ddx / dlen;
				dy += ddy / dlen;
			}
		}


		void update() {
			if (!fixed) {      
				x += Utils.globalProcessing.constrain(dx, -5, 5);
				y += Utils.globalProcessing.constrain(dy, -5, 5);

				x = Utils.globalProcessing.constrain(x, 0, Utils.globalProcessing.width);
				y = Utils.globalProcessing.constrain(y, 0, Utils.globalProcessing.height);
			}
			dx /= 2;
			dy /= 2;
		}


		void draw() {
			Utils.globalProcessing.fill(nodeColor);
			Utils.globalProcessing.stroke(0);
			Utils.globalProcessing.strokeWeight((float)0.5);

			Utils.globalProcessing.ellipse(x, y, count, count);
			float w = Utils.globalProcessing.textWidth(label);

			if (count > w+2) {
				Utils.globalProcessing.fill(0);
				Utils.globalProcessing.textAlign(Utils.globalProcessing.CENTER, Utils.globalProcessing.CENTER);
				Utils.globalProcessing.text(label, x, y);
			}
		}
	}



	//Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
	//Based on the GraphLayout example by Sun Microsystems.

	ArrayList<Node> nodes;
	ArrayList<Edge> edges;
	HashMap nodeTable;


	public GraphNetwork(){
		nodes = new ArrayList<Node>();
		edges = new ArrayList<Edge>();
		nodeTable = new HashMap();
	}

	static final int nodeColor   = 120;
	static final int selectColor = 240;
	static final int fixedColor  = 175;
	static final int edgeColor   = 55;

	void addEdge(String fromLabel, String toLabel) {
		// Filter out unnecessary words
		if (ignoreWord(fromLabel) || ignoreWord(toLabel)) return;

		Node from = findNode(fromLabel);
		Node to = findNode(toLabel);
		from.increment();
		to.increment();

		for (int i = 0; i < edges.size(); i++) {
			if (edges.get(i).from == from && edges.get(i).to == to) {
				edges.get(i).increment();
				return;
			}
		} 

		Edge e = new Edge(from, to);
		e.increment();
		edges.add(e);
	}


	String[] ignore = { "a", "of", "the", "i", "it", "you", "and", "to" };

	boolean ignoreWord(String what) {
		for (int i = 0; i < ignore.length; i++) {
			if (what.equals(ignore[i])) {
				return true;
			}
		}
		return false;
	}



	Node findNode(String label) {
		label = label.toLowerCase();
		Node n = (Node) nodeTable.get(label);
		if (n == null) {
			return addNode(label);
		}
		return n;
	}


	Node addNode(String label) {
		Node n = new Node(label); 
		nodeTable.put(label, n);
		nodes.add(n);  
		return n;
	}


	void draw() {
		if (record) {
			Utils.globalProcessing.beginRecord(Utils.globalProcessing.PDF, "output.pdf");
		}

		Utils.globalProcessing.background(255);

		for (int i = 0 ; i < edges.size() ; i++) {
			edges.get(i).relax();
		}
		for (int i = 0; i < edges.size(); i++) {
			nodes.get(i).relax();
		}
		for (int i = 0; i < edges.size(); i++) {
			nodes.get(i).update();
		}
		for (int i = 0 ; i < edges.size() ; i++) {
			edges.get(i).draw();
		}
		for (int i = 0 ; i < edges.size() ; i++) {
			nodes.get(i).draw();
		}

		if (record) {
			Utils.globalProcessing.endRecord();
			record = false;
		}
	}


	boolean record;

	void keyPressed() {
		if (Utils.globalProcessing.key == 'r') {
			record = true;
		}
	}


	Node selection; 


	void mousePressed() {
		// Ignore anything greater than this distance
		float closest = 20;
		for (int i = 0; i < nodes.size(); i++) {
			Node n = nodes.get(i);
			float d = Utils.globalProcessing.dist(Utils.globalProcessing.mouseX, Utils.globalProcessing.mouseY, n.x, n.y);
			if (d < closest) {
				selection = n;
				closest = d;
			}
		}
		if (selection != null) {
			if (Utils.globalProcessing.mouseButton == Utils.globalProcessing.LEFT) {
				selection.fixed = true;
			} else if (Utils.globalProcessing.mouseButton == Utils.globalProcessing.RIGHT) {
				selection.fixed = false;
			}
		}
	}


	void mouseDragged() {
		if (selection != null) {
			selection.x = Utils.globalProcessing.mouseX;
			selection.y = Utils.globalProcessing.mouseY;
		}
	}


	void mouseReleased() {
		selection = null;
	}

}